#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>

#include "fileman.h"
#include "mvfs.h"
#include "onlinestor.h"

extern FileManager *pMan;

struct OnlineFile;

int MVFS::getattr(const char *path, struct stat *stbuf) {
    memset(stbuf, 0, sizeof(struct stat));
    stbuf->st_nlink = 1;
    int len = strlen(path);
    int res, size, folder;

    if (strcmp(path, "/") == 0) {
    	stbuf->st_mode = S_IFDIR | 0777;
	res = 0;
    } else if (pMan -> fileinfo(path, &size, &folder) >= 0) {
        if (folder == 1) {
        	stbuf->st_mode = S_IFDIR | 0777;
        	stbuf->st_size = 0;
        } else {
        	stbuf->st_mode = S_IFREG | 0755;
        	stbuf->st_size = size;
        }
        res = 0;
    } else {
        res = -ENOENT;
 	}

    return res;
}

int MVFS::mkdir (const char *path, mode_t mode) {
	const char *p = strrchr(path, '/');
	int len = p - path + 1, res = 0;
	char *dir = (char *)malloc(sizeof(char) * (len + 1));
	memcpy(dir, path, len);
	dir[len] = '\0';

	if (0 != pMan -> mkdir(dir, p + 1)) {
		res = -EIO;
	}

	free(dir);
	return res;
}

int MVFS::readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
	filler(buf, ".", NULL, 0);
	filler(buf, "..", NULL, 0);

	int size;
	NetFile *f;

	f = pMan -> lsdir(path, &size);
	if (f) {
		for (int i = 0; i < size; i++) {
			filler(buf, f[i].fileName, NULL, 0);
		}
	}
	return 0;
}

int MVFS::open(const char *path, struct fuse_file_info *fi) {
	int ret = 0;

	if (NULL == path)
		return -1;

	OnlineFile *f = pMan -> open(path);

	if (NULL == f)
		return -1;
	if ((fi->flags & O_ACCMODE) == O_RDONLY) {
		fi -> fh = (unsigned long)f;
		ret = 0;
	} else if (fi->flags & (O_RDWR | O_WRONLY)) {
		fi -> fh = (unsigned long)f;
		ret = 0;
	} else {
		ret = -ENOTSUP;
	}
	return ret;
}

int MVFS::rmdir (const char *path) {
	const char *p = strrchr(path, '/');
	int len = p - path + 1, res = 0;
	char *dir = (char *)malloc(sizeof(char) * (len + 1));
	memcpy(dir, path, len);
	dir[len] = '\0';

	if (0 != pMan -> rmdir(dir, p + 1)) {
		res = -EIO;
	}

	free(dir);
	return res;
}

int MVFS::rename (const char *from, const char *to) {
	int res = 0;
	if (pMan -> move(from, to)) {
		res = 0;
	} else {
		res = -EIO;
	}
	return res;
}

int MVFS::truncate (const char *path, off_t offset) {
	if (pMan->truncate(path,offset) )
		return 0;
	return -EIO;
}

int MVFS::read(const char *path, char *rbuf, size_t size, off_t offset, struct fuse_file_info *fi) {
	int ret = 0;

	if (NULL == path || NULL == rbuf)
		return -1;

	OnlineFile *f = (OnlineFile*)fi -> fh;
	if (f == NULL)
		return -EACCES;

	ret = pMan -> read(f, rbuf, offset, size);
	
	if (ret == 0) return size;

	return ret;
}

int MVFS::write(const char *path, const char *wbuf, size_t size, off_t offset, struct fuse_file_info *fi) {
	int ret = 0;

	if (NULL == path || NULL == wbuf)
		return -1;
	OnlineFile *f = (OnlineFile*)fi -> fh;
	if (f == NULL)
		return -EACCES;

	ret = pMan -> write(f, wbuf,offset, size);
	if (ret < 0)
		ret = -EIO;

	if (ret == 0) return size;

	return ret;
}

int MVFS::create(const char *path, mode_t mode, struct fuse_file_info *fi) {
	int ret = 0;

	if (NULL == path)
		return -1;

	OnlineFile *f = pMan -> open(path);

	if (NULL == f)
		return -1;
	if ((fi->flags & O_ACCMODE) == O_RDONLY) {
		fi -> fh = (unsigned long)f;
		ret = 0;
	} else if (fi->flags & (O_RDWR | O_WRONLY)) {
		fi -> fh = (unsigned long)f;
		ret = 0;
	} else {
		ret = -ENOTSUP;
	}
	return ret;
}

int MVFS::release(const char *path, struct fuse_file_info *fi) {
	int ret = 0;

	if (NULL == path)
		return -1;

	OnlineFile *f = (OnlineFile*)fi -> fh;

	if (NULL == f)
		return -1;

	pMan -> close(f);
	
	return ret;
}

static int MVFS::unlink(const char *path) {
	if (pMan -> del(path) == 0) {
		return 0;
	} else {
		return -EIO;
	}
}

static void* MVFS::init(fuse_conn_info* fci) {
	return NULL;
}

static void MVFS::destroy(void *ptr) {
}
